﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Threading;

namespace TrueCode.Core.Diagnostics
{
	public class LogCategory
	{
		public static readonly String Critical			= "Critical";
		public static readonly String Error				= "Error";
		public static readonly String Information		= "Information";
		public static readonly String CallTraceEnter	= "CallTraceEnter";
		public static readonly String CallTraceExit		= "CallTraceExit";
		public static readonly String CallTrace			= "CallTrace";
	}

	public class Log
	{
		#region Fields
		public readonly Int32 Indent;
		public readonly DateTime Time;
		public readonly String Subsystem;
		public readonly Type SenderType;
		public readonly String SenderInfo;
		public readonly String[] Categories;
		public readonly String Message;
		public readonly Exception Exception;
		public readonly IDictionary<String, Object> ExtendedInformation;

		public readonly String MachineName;
		public readonly String ThreadName;
		#endregion

		#region .ctors
		public Log(Int32 indent, String subSystem, Type senderType, String senderInfo, String message, IDictionary<String, Object> extendedInformation, Exception exception, params String[] categories)
		{
			Indent = indent;
			Time = DateTime.Now;
			Subsystem = subSystem;
			SenderType = senderType;
			SenderInfo = senderInfo;
			Message = message;
			ExtendedInformation = extendedInformation;
			Exception = exception;
			Categories = categories;

			if(Exception != null)
			{
				Exception.Data.ForEach< DictionaryEntry >(pair => ExtendedInformation.Add(pair.Key.ToString(), pair.Value));
			}

			ThreadName = Thread.CurrentThread.Name ?? Thread.CurrentThread.ManagedThreadId.ToString();
			MachineName = Environment.MachineName;
		}
		#endregion

		public override string ToString()
		{
			var stringBuilder = new StringBuilder();

			for(var i = 0; i < Indent; ++i)
			{
				stringBuilder.Append(Indent);
			}
			var indent = stringBuilder.ToString();
			stringBuilder.Append(Message);
			stringBuilder.Append("   ");
			stringBuilder.Append(SenderInfo);


			stringBuilder.Append("   ");
			stringBuilder.Append(String.Format("Subsystem = {0}", Subsystem));

			stringBuilder.Append("   ");
			stringBuilder.Append("Category = ");
			foreach(String category in Categories)
			{
				stringBuilder.Append(String.Format(" {0},", category));
			}
			stringBuilder.AppendLine();

			if(ExtendedInformation != null)
			{
				foreach(KeyValuePair<String, Object> pair in ExtendedInformation)
				{
					stringBuilder.Append(indent);
					stringBuilder.AppendFormat("{0}: {1}", pair.Key, pair.Value);
					stringBuilder.AppendLine();
				}
			}

			return stringBuilder.ToString();
		}
	}

	public interface IDiagnostic
	{
		#region Logging
		void AssociateCallingAssemblyWithSubsystem(String subsystem);
		void AssociateCallingAssemblyWithSubsystem(Assembly assembly, String subsystem);
		Int32 GetCurrentThreadIndent();
		void SetCurrentThreadIndent(Int32 indent);
		void Indent();
		void Unindent();

		/// <summary>
		/// Создает и возвращает объект, который запишет информацию о входе и выходе в некоторый блок.
		/// При создании он записывает информацию, о том, что вошли в блок. При вызове метода Dispose о том, что вышли.
		/// Рекомендуется использовать с оператором using.
		/// </summary>
		/// <param name="sender">Объект запрашивающий Trace</param>
		/// <param name="parameters">Необязательный набор дополнительных параметров. Рассматривается как набор пар "Имя", "Значение"</param>
		/// <returns>Возвращает объект, который запишет информацию о входе и выходе в некоторый блок.</returns>
		IDisposable GetTrace(Object sender, params Object[] parameters);
		void WriteException(Object sender, Exception exception);

		/// <summary>
		/// Записывает в журнал информацию об исключении.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="exception"></param>
		/// <param name="parameters">Список интерпретируется как набор пар "Имя", "Значение".
		/// Проверки на четность количества параметров не производится.</param>
		void WriteException(Object sender, Exception exception, params Object[] parameters);

		void WriteException(Object sender, String message, Exception exception, params Object[] parameters);

		/// <summary>
		/// Записывает в журнал информацию об исключении.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="message"></param>
		/// <param name="exception"></param>
		/// <param name="categoriesCount"></param>
		/// <param name="parameters">Список интерпретируется как набор пар "Имя", "Значение" перед которым идет <paramref="categoriesCount"/>
		/// строк интерпретируемых как категории.
		/// Проверки на четность количества параметров не производится.</param>
		void WriteException(
			Object sender, String message, Exception exception, Int32 categoriesCount, params Object[] parameters);

		void WriteException(Object sender, String message, Exception exception, params String[] categories);
		void WriteException(Exception exception, Object sender, String message, TraceEventType logEntryType);

		void WriteException(
			String subsystem, Object sender, String message, Exception exception, params Object[] parameters);

		void WriteException(String subsystem, Object sender, String message, Exception exception);

		void WriteException(
			String subsystem, Object sender, String message, Exception exception, TraceEventType logEntryType);

		void WriteException(
			String subsystem,
			Object sender,
			String message,
			Exception exception,
			TraceEventType logEntryType,
			params String[] categories);

		/// <summary>
		/// Записывает в журнал информацию об исключении.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="message"></param>
		/// <param name="exception"></param>
		/// <param name="categoriesCount"></param>
		/// <param name="parameters">Список интерпретируется как набор пар "Имя", "Значение" перед которым идет <paramref="categoriesCount"/>
		/// строк интерпретируемых как категории.
		/// Проверки на четность количества параметров не производится.</param>
		void WriteException(
			String subsystem,
			Object sender,
			String message,
			Exception exception,
			TraceEventType logEntryType,
			Int32 categoriesCount,
			params Object[] parameters);

		void Write(Object sender, String message, TraceEventType logEntryType);
		void Write(Object sender, String message, TraceEventType logEntryType, params String[] categories);
		void Write(Object sender, String message, TraceEventType logEntryType, params Object[] parameters);

		void Write(
			Object sender, String message, TraceEventType logEntryType, Int32 categoriesCount, params Object[] parameters);

		void Write(
			String subsystem, Object sender, String message, TraceEventType logEntryType, params String[] categories);

		void Write(
			String subsystem,
			Object sender,
			String message,
			TraceEventType logEntryType,
			Int32 categoriesCount,
			params Object[] parameters);

		void Write(TraceEventType logEntryType, Log log);
		#endregion
	}
}
